﻿using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using Kenly.DBFramework.Reflection;
using Kenly.DBFramework;
using Kenly.DBFramework.Gateway;

namespace Kenly.DBFramework.Gateway
{
    /// <summary>
    /// The gateway to a database table.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class OracleTableGatewayUtility<T> : TableGatewayUtilityBase<T> where T : TableGateway<T>, new()
    {

        public OracleTableGatewayUtility(DatabaseType databaseType, string connectionString)
            : base(databaseType, connectionString)
        {
        }

        public override int AddNew(ITableCommandExecutor tableCommandExecutor, ICommandReflector commandReflector, T entity, bool returnIdentity)
        {
            List<string> propertyNames = entity.ChangedPropertyNames;
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                return 0;
            }

            List<IDataParameter> paras = ParameterReflection.Insert(base.DatabaseType, entity, propertyNames);
            IDataParameter idParameter = GetIdentityParameter(tableCommandExecutor, commandReflector, entity, returnIdentity);
            if (idParameter != null)
            {
                paras.Add(idParameter);
            }
            string insertCommandText = commandReflector.Insert(typeof(T), false, propertyNames);
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(insertCommandText, false, paras);
            if (affectedRowCount > 0)
            {
                entity.ClearChangedPropertyNames();
            }
            return affectedRowCount;
        }

        private static object GetNextIdentity(ITableCommandExecutor tableCommandExecutor, ICommandReflector commandReflector)
        {
            string commandText = commandReflector.SelectNextIdentity(typeof(T));
            return tableCommandExecutor.ExecuteScalar(commandText, false);
        }

        private IDataParameter GetIdentityParameter(ITableCommandExecutor tableCommandExecutor, ICommandReflector commandReflector,
            T entity, bool returnIdentity)
        {
            TableAttribute tableAttribute = AttributeUtility.GetTableAttribute(typeof(T));
            string identityName = IdentityUtility.GetIdentityName(typeof(T));
            if (string.IsNullOrEmpty(identityName) || string.IsNullOrEmpty(tableAttribute.IdSequence))
            {
                return null;
            }

            object nextIdentity = GetNextIdentity(tableCommandExecutor, commandReflector);
            IDataParameter idParameter = ParameterReflection.GetIdentityParameter(base.DatabaseType, entity, false);
            if (nextIdentity != null)
            {
                idParameter.Value = nextIdentity;
                if (returnIdentity)
                {
                    IdentityUtility.SetIdentityValue(entity, nextIdentity);
                }
            }

            return idParameter;
        }

        protected override int AddNew(T entity, bool returnIdentity, bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            List<string> propertyNames = entity.ChangedPropertyNames;
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                return 0;
            }

            ICommandReflector commandReflector = base.CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = base.CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);

            List<IDataParameter> paras = ParameterReflection.Insert(base.DatabaseType, entity, propertyNames);
            IDataParameter idParameter = GetIdentityParameter(tableCommandExecutor, commandReflector, entity, returnIdentity);
            if (idParameter != null)
            {
                paras.Add(idParameter);
            }

            string insertCommandText = commandReflector.Insert(typeof(T), false, propertyNames);

            if (!commitTransaction)
            {
                tableCommandExecutor.BeginTransaction();
            }
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(insertCommandText, false, paras);
            if (affectedRowCount > 0)
            {
                entity.ClearChangedPropertyNames();
            }
            return affectedRowCount;
        }

        protected override int AddNew(IList<T> entities, bool returnIdentity, bool commitTransaction, out Transaction transaction)
        {
            transaction = null;
            if (entities == null || entities.Count <= 0)
            {
                return 0;
            }

            string insertStatement = "";
            List<IDataParameter> paras = null;
            IDataParameter idParameter;
            List<string> propertyNames;
            int affectedRowCount = 0;

            ICommandReflector commandReflector = base.CreateCommandReflector();
            ITableCommandExecutor tableCommandExecutor = base.CreateCommandExecutor();
            transaction = new Transaction(tableCommandExecutor);

            try
            {
                tableCommandExecutor.BeginTransaction();
                foreach (T entity in entities)
                {
                    propertyNames = entity.ChangedPropertyNames;
                    if (propertyNames == null || propertyNames.Count <= 0)
                    {
                        continue;
                    }
                    paras = ParameterReflection.Insert(base.DatabaseType, entity, propertyNames);
                    idParameter = GetIdentityParameter(tableCommandExecutor, commandReflector, entity, returnIdentity);
                    if (idParameter != null)
                    {
                        paras.Add(idParameter);
                    }
                    insertStatement = commandReflector.Insert(typeof(T), false, propertyNames);
                    if (tableCommandExecutor.ExecuteNonQuery(insertStatement, false, paras) > 0)
                    {
                        affectedRowCount++;
                        entity.ClearChangedPropertyNames();
                    }
                }

                if (commitTransaction)
                {
                    tableCommandExecutor.Commit();
                }
                return affectedRowCount;
            }
            catch (Exception ex)
            {
                tableCommandExecutor.Rollback();
                throw new CommandException(insertStatement, MsgResource.ExecuteNonQueryFailed, ex);
            }
        }


    }


}